/*
 * Created on Jan 15, 2013
 * Created by Paul Gardner
 * 
 * Copyright 2013 Azureus Software, Inc.  All rights reserved.
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 of the License only.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
 */

package com.aelitis.azureus.core.util.dns;

import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;

import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;

import org.gudy.azureus2.core3.util.Debug;

import com.aelitis.azureus.core.util.DNSUtils;
import com.aelitis.azureus.core.util.DNSUtils.DNSUtilsIntf;

public class DNSUtilsImpl implements DNSUtilsIntf {
    private static String getFactory() {
        return (System.getProperty("azureus.dns.context.factory", "com.sun.jndi.dns.DnsContextFactory"));
    }

    public DNSDirContextImpl getInitialDirContext()

    throws NamingException {
        Hashtable env = new Hashtable();

        env.put(Context.INITIAL_CONTEXT_FACTORY, getFactory());

        return (new DNSDirContextImpl(new InitialDirContext(env)));

    }

    public DNSDirContextImpl getDirContextForServer(String dns_server_ip)

    throws NamingException {
        Hashtable env = new Hashtable();

        env.put(Context.INITIAL_CONTEXT_FACTORY, getFactory());

        env.put(Context.PROVIDER_URL, "dns://" + dns_server_ip + "/");

        return (new DNSDirContextImpl(new InitialDirContext(env)));
    }

    public Inet6Address getIPV6ByName(String host)

    throws UnknownHostException {
        List<Inet6Address> all = getAllIPV6ByName(host);

        return (all.get(0));
    }

    public List<Inet6Address> getAllIPV6ByName(String host)

    throws UnknownHostException {
        List<Inet6Address> result = new ArrayList<Inet6Address>();

        try {
            DirContext context = getInitialDirContext().ctx;

            Attributes attrs = context.getAttributes(host, new String[] { "AAAA" });

            if (attrs != null) {

                Attribute attr = attrs.get("aaaa");

                if (attr != null) {

                    NamingEnumeration values = attr.getAll();

                    while (values.hasMore()) {

                        Object value = values.next();

                        if (value instanceof String) {

                            try {
                                result.add((Inet6Address) InetAddress.getByName((String) value));

                            } catch (Throwable e) {
                            }
                        }
                    }
                }
            }
        } catch (Throwable e) {
        }

        if (result.size() > 0) {

            return (result);
        }

        throw (new UnknownHostException(host));
    }

    public List<InetAddress> getAllByName(String host)

    throws UnknownHostException {
        try {
            return (getAllByName(getInitialDirContext(), host));

        } catch (NamingException e) {

            throw (new UnknownHostException(host));
        }
    }

    public List<InetAddress> getAllByName(DNSUtils.DNSDirContext context, String host)

    throws UnknownHostException {
        List<InetAddress> result = new ArrayList<InetAddress>();

        try {
            String[] attributes = new String[] { "A", "AAAA" };

            Attributes attrs = ((DNSDirContextImpl) context).ctx.getAttributes(host, attributes);

            if (attrs != null) {

                for (String a : attributes) {

                    Attribute attr = attrs.get(a);

                    if (attr != null) {

                        NamingEnumeration values = attr.getAll();

                        while (values.hasMore()) {

                            Object value = values.next();

                            if (value instanceof String) {

                                try {
                                    result.add(InetAddress.getByName((String) value));

                                } catch (Throwable e) {
                                }
                            }
                        }
                    }
                }
            }
        } catch (Throwable e) {
        }

        if (result.size() > 0) {

            return (result);
        }

        throw (new UnknownHostException(host));
    }

    private static Map<String, String> test_records = new HashMap<String, String>();

    static {
        test_records.put("test1.test.null", "BITTORRENT DENY ALL");
        test_records.put("test2.test.null", "BITTORRENT");
        test_records.put("test3.test.null", "BITTORRENT TCP:1 TCP:2 UDP:1 UDP:2");
        test_records.put("test4.test.null", "BITTORRENT TCP:3");
        test_records.put("test5.test.null", "BITTORRENT UDP:4");
    }

    public List<String> getTXTRecords(String query) {
        // System.out.println( "DNSTXTQuery: " + query );

        List<String> result = new ArrayList<String>();

        String test_reply = test_records.get(query);

        if (test_reply != null) {

            result.add(test_reply);

            return (result);
        }

        DirContext context = null;

        try {
            context = getInitialDirContext().ctx;

            Attributes attrs = context.getAttributes(query, new String[] { "TXT" });

            NamingEnumeration n_enum = attrs.getAll();

            while (n_enum.hasMoreElements()) {

                Attribute attr = (Attribute) n_enum.next();

                NamingEnumeration n_enum2 = attr.getAll();

                while (n_enum2.hasMoreElements()) {

                    String attribute = (String) n_enum2.nextElement();

                    if (attribute != null) {

                        attribute = attribute.trim();

                        if (attribute.startsWith("\"")) {

                            attribute = attribute.substring(1);
                        }

                        if (attribute.endsWith("\"")) {

                            attribute = attribute.substring(0, attribute.length() - 1);
                        }

                        if (attribute.length() > 0) {

                            result.add(attribute);
                        }
                    }
                }
            }

        } catch (Throwable e) {

            // e.printStackTrace();

        } finally {

            if (context != null) {

                try {
                    context.close();

                } catch (Throwable e) {
                }
            }
        }

        return (result);
    }

    public String getTXTRecord(String query)

    throws UnknownHostException {
        DirContext context = null;

        try {
            context = getInitialDirContext().ctx;

            Attributes attrs = context.getAttributes(query, new String[] { "TXT" });

            NamingEnumeration n_enum = attrs.getAll();

            while (n_enum.hasMoreElements()) {

                Attribute attr = (Attribute) n_enum.next();

                NamingEnumeration n_enum2 = attr.getAll();

                while (n_enum2.hasMoreElements()) {

                    String attribute = (String) n_enum2.nextElement();

                    if (attribute != null) {

                        attribute = attribute.trim();

                        if (attribute.startsWith("\"")) {

                            attribute = attribute.substring(1);
                        }

                        if (attribute.endsWith("\"")) {

                            attribute = attribute.substring(0, attribute.length() - 1);
                        }

                        if (attribute.length() > 0) {

                            return (attribute);
                        }
                    }
                }
            }

            throw (new UnknownHostException("DNS query returned no results"));

        } catch (Throwable e) {

            throw (new UnknownHostException("DNS query failed:" + Debug.getNestedExceptionMessage(e)));

        } finally {

            if (context != null) {

                try {
                    context.close();

                } catch (Throwable e) {
                }
            }
        }
    }

    public static class DNSDirContextImpl implements DNSUtils.DNSDirContext {
        private DirContext ctx;

        private DNSDirContextImpl(DirContext _ctx) {
            ctx = _ctx;
        }

        public String getString() {
            try {
                return (String.valueOf(ctx.getEnvironment()));

            } catch (Throwable e) {

                return (Debug.getNestedExceptionMessage(e));
            }
        }
    }

    public static void main(String[] args) {
        try {
            // List<String> records = getTXTRecords( "tracker.openbittorrent.com" );
            /*
             * List<String> records = getTXTRecords( "www.ibm.com" ); for ( String record: records ){ System.out.println( record ); }
             */

            DNSUtilsImpl impl = new DNSUtilsImpl();

            DNSUtils.DNSDirContext ctx = impl.getDirContextForServer("8.8.4.4");

            System.out.println(impl.getAllByName(ctx, "www.google.com"));

        } catch (Throwable e) {

            e.printStackTrace();
        }
    }
}
